using UnityEngine;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Threading;
using System.Net;

public class Planet : ISerializable
{
	// enums
	#region enums
	public enum PlanetSize
	{
		None = 0,				// Gas giant or empty orbit
		Tiny = 1,
		Small = 2,
		Medium = 3,
		Large = 4,
		Huge = 5,
		Other = 6				// Special type planet
	}

	public enum PlanetClimate
	{
		Uninhabitable = 0,
		Polluted = 1,
		Barren = 2,
		Desert = 3,
		Tundra = 4,
		Ocean = 5,
		Swamp = 6,
		Terran = 7,
		Gaia = 8
	}
	
	public enum PlanetMineral
	{
		UltraPoor = 1,
		Poor = 2,
		Abundant = 3,
		Rich = 4,
		UltraRich = 5
	}

	public enum PlanetLifeform				// planet's lifeform
	{
		None = 0,
		Low = 1,
		Sea = 2,
		Land = 3,
		Air = 4,
		High = 5
	}

  public enum PlanetType
  {
      Extreme = 1,
      Poor = 2,
      Normal = 3,
      Rich = 4,
      Ultra
  }

	public enum PlanetState
	{
		None = 0x00000000,
		LowG = 0x00000001,						// Low Gravity
		BadGeoPole = 0x00000002,			// bad geographical pole
		BadMagPole = 0x00000004,			// bad magnetic pole
		ToxicGas = 0x00000008,
		Radiation = 0x00000010,
		HeavyG = 0x00000020,
		GemRich = 0x00000040,
		GoldRich = 0x00000080,
		MetalRich = 0x00000100,				// rare metal rich
		WithVirus = 0x00000200,
		WithBeast = 0x00000400,				// with dangerous animals and creatures
		WithIntel = 0x00000800,				// with intelligent species
		WithArtifact = 0x00001000,
		WithGuardian = 0x00002000			// planet is with guardian needed to be defeated to occupy
	}

	public enum PlanetNatives
	{
		None = 0x0000,
		Magic = 0x0001,								// natives are with magical power
		Hostle = 0x0002,							// natives are hostle species
		Telepathy = 0x0004,				    // natives are with telepathy ability
		Foreknowledge	= 0x0008,		    // natives are with forknowledge
		RangeAttack = 0x0010,			    // natives are capable of range attack
		SpaceFight = 0x0020,			    // natives are capable of space fighting
	}

	public enum PlanetStatus
	{
		None = 0,
		Occupied = 1,							// by enemies
		Conquered = 2,						// by player
		Destroyed = 3,
	}

	public enum PlanetNaming
	{
		I = 1,
		II = 2,
		III = 3,
		IV = 4,
		V = 5,
		VI = 6
	}
	#endregion


	// Declaration
	private string m_PlanetID;
	private string m_PlanetName;
	private PlanetSize m_Size;
	private PlanetClimate m_Climate;
	private PlanetMineral m_Mineral;
	private PlanetLifeform m_Lifeform;
	private PlanetState m_PlanetState = 0x00000000;
	private PlanetNatives m_Natives = 0x0000;

	private PlanetStatus m_Status;
	private PlanetState m_Richness;
	private int m_Orbit;
	private bool m_Intel;
	private bool m_Giant;

	private bool m_AutoMode;					// whether planet development is in auto-mode

	private StarSystem m_Solar;				// solar system to which this planet belong
	private Kingdom m_Kingdom;				// kingdom who occupies this planet
	private bool m_HugeShip;					// whether planet can build large ships

	private double m_ProdPower;				// planet's productivity power
	private double m_DevPower;				// planet's development power
	private double m_FarmPower;				// planet's farming power

	private double m_BaseProd;				// base productivity of planet
	private double m_BaseDev;					// base development power of planet
	private double m_BaseFarm;				// base farming power of planet

	private double m_MaxPop;					// planet maximum population
	private double[] m_Population;		// current planet population. {sci, dev, farmer}
	private double m_GoldAmt;					// gold amount generated per turn
	private double m_GoldFactor;			// in terms of percentage, total gold = m_GoldAmt * m_GoldFactor

	private double m_BaseMarine;			// planet base marine units
	private double m_BaseDefense;			// planet's base defending power

	private double m_DefendPow;				// planet's defending power of this turn

	// visual planet related
	private double m_Speed;						// planet rotational speed in orbit

	private List<PlanetFacility> m_Facilities = new List<PlanetFacility>();			// planet building facilities

	private List<IBuildable> m_DevObj = new List<IBuildable>();
	private List<IBuildable> m_Dispatch = new List<IBuildable>();
	private List<IBuildable> m_Warship = new List<IBuildable>();

	// arraylist is used because in special events, more than one facilities may be built in one turn
	private List<PlanetFacility> m_NewlyOwned = new List<PlanetFacility>();			// facilities developed and owned in this turn


	// visual related properties
	private GameObject m_Star;			// the visual star
	private GameObject m_Sun;				// the sunlight object of which the star follows its orbits


	// ISerializable
	public ulong Serial
	{
		get
		{
			ulong val;
			bool parsed = UInt64.TryParse(m_PlanetID, out val);

			if (!parsed)
				val = 0;

			return val;
		}
	}

	// getters and setters
	#region getters and setters
	public PlanetSize Size
	{
		get { return m_Size; }
		set { m_Size = value; }
	}


	public string PlanetID
	{
		get { return m_PlanetID; }
		set { m_PlanetID = value; }
	}

	public string PlanetName
	{
		get { return m_PlanetName; }
		set { m_PlanetName = value; }
	}

	public PlanetClimate Climate
	{
		get { return m_Climate; }
		set { m_Climate = value; }
	}

	public PlanetMineral Mineral
	{
		get { return m_Mineral; }
		set { m_Mineral = value; }
	}

	public PlanetLifeform Lifeform
	{
		get { return m_Lifeform; }
		set { m_Lifeform = value; }
	}

	public int Orbit
	{
		get { return m_Orbit; }
		set { m_Orbit = value; }
	}

	public PlanetState PlanetFlag
	{
		get { return m_PlanetState; }
		set { m_PlanetState = value; }
	}

	public PlanetNatives NativesFlag
	{
		get { return m_Natives; }
		set { m_Natives = value; }
	}

	public PlanetStatus Status
	{
		get { return m_Status; }
		set { m_Status = value; }
	}

	public PlanetState Richness
	{
		get { return m_Richness; }
		set { m_Richness = value; }
	}

	public bool Intel
	{
		get { return m_Intel; }
		set { m_Intel = value; }
	}

	public bool Giant
	{
		get { return m_Giant; }
		set { m_Giant = value; }
	}

	public bool AutoMode
	{
		get { return m_AutoMode; }
		set { m_AutoMode = value; }
	}

	public StarSystem Solar
	{
		get { return m_Solar; }
		set { m_Solar = value; }
	}

	public Kingdom Kingdom
	{
		get { return m_Kingdom; }
		set { m_Kingdom = value; }
	}

	public bool HugeShip
	{
		get { return m_HugeShip; }
		set { m_HugeShip = value; }
	}

	public double ProdPower
	{
		get { return m_ProdPower; }
		set { m_ProdPower = value; }
	}

	public double DevPower
	{
		get { return m_DevPower; }
		set { m_DevPower = value; }
	}

	public double FarmPower
	{
		get { return m_FarmPower; }
		set { m_FarmPower = value; }
	}

	public double BaseProd
	{
		get { return m_BaseProd; }
		set { m_BaseProd = value; }
	}

	public double BaseDev
	{
		get { return m_BaseDev; }
		set { m_BaseDev = value; }
	}

	public double BaseFarm
	{
		get { return m_BaseFarm; }
		set { m_BaseFarm = value; }
	}

	public double MaxPop
	{
		get { return m_MaxPop; }
		set { m_MaxPop = value; }
	}

	public double[] Population
	{
		get { return m_Population; }
		set { m_Population = value; }
	}

	public double GoldAmt
	{
		get { return m_GoldAmt; }
		set { m_GoldAmt = value; }
	}

	public double GoldFactor
	{
		get { return m_GoldFactor; }
		set { m_GoldFactor = value; }
	}

	public double BaseMarine
	{
		get { return m_BaseMarine; }
		set { m_BaseMarine = value; }
	}

	public double BaseDefense
	{
		get { return m_BaseDefense; }
		set { m_BaseDefense = value; }
	}

	public double DefendPow
	{
		get { return m_DefendPow; }
		set { m_DefendPow = value; }
	}


	// visual object related
	public double Speed
	{
		get { return m_Speed; }
		set { m_Speed = value; }
	}

	public List<PlanetFacility> Facilities
	{
		get { return m_Facilities; }
		set { m_Facilities = value; }
	}

	public List<IBuildable> DevObj
	{
		get { return m_DevObj; }
		set { m_DevObj = value; }
	}

	public List<IBuildable> Dispatch
	{
		get { return m_Dispatch; }
		set { m_Dispatch = value; }
	}

	public List<IBuildable> Warship
	{
		get { return m_Warship; }
		set { m_Warship = value; }
	}

	public List<PlanetFacility> NewlyOwned
	{
		get { return m_NewlyOwned; }
		set { m_NewlyOwned = value; }
	}

	// visual related
	public GameObject Star
	{
		get { return m_Star; }
		set { m_Star = value; }
	}

	public GameObject Sun
	{
		get { return m_Sun; }
		set { m_Sun = value; }
	}
	#endregion


	// Constructors of class Planet
	public Planet()
	{
		m_Status = PlanetStatus.None;

		// initialize properties
		m_HugeShip = false;
		m_AutoMode = false;

		m_Population = new double[] { 0, 0, 0 };
	}

	public Planet(int sid, string sname, int orbit, Point3D loc, bool isGas, PlanetType ptype) : this()
	{
		PlanetNaming pn = (PlanetNaming)orbit;

		m_PlanetID = sid.ToString("D3") + orbit.ToString("D2");
		m_PlanetName = sname.Trim() + " " + pn.ToString();
		m_Orbit = orbit;
		m_Giant = isGas;

		GeneratePlanet(orbit, loc, isGas, ptype);
	}

	// to pass parameters as object
	public Planet( object state ) : this()
	{
		object[] aState = (object[])state;
		int sid = (int)aState[0];
		m_PlanetName = (string)aState[1];
		int orbit = (int)aState[2];
		Point3D loc = (Point3D)aState[3];
		bool isGas = (bool)aState[4];
		PlanetType ptype = (PlanetType)aState[5];

		PlanetNaming pn = (PlanetNaming)orbit;

		m_PlanetID = sid.ToString("D3") + orbit.ToString("D2");
		m_PlanetName = m_PlanetName.Trim() + " " + pn.ToString();
		m_Orbit = orbit;
		m_Giant = isGas;

		GeneratePlanet(orbit, loc, isGas, ptype);
	}


	// sub classes
	public class PlanetFacility : IBuildable					// controlling both static and non-static version facilities
	{
		public GameEnums.Facilities pName;
		public string DisplayName { get; set; }
		public Building pBuild;
		public double BuildCost	{ get; set; }						// devpower needed to develop
		public double OwnCost { get; set; }							// current accumulated devpower
		public bool pInEffect = false;									// whether facility is in effect
		public bool pObsolete = false;
		public bool pRepeat = false;

		public PlanetFacility(GameEnums.Facilities name, Building building, bool effecitve, bool obsolete)
		{
			pName = name;
			DisplayName = building.DispName;
			pBuild = building;
			BuildCost = building.BuildCost;
			OwnCost = 0.0;
			pInEffect = effecitve;
			pObsolete = obsolete;
		}

	}

	public class FacilitySorter : IComparer<PlanetFacility>
	{
		public int Compare(PlanetFacility x, PlanetFacility y)
		{
			int ix = Convert.ToInt32(x.pName.ToString()[0]);
			int iy = Convert.ToInt32(y.pName.ToString()[0]);

			return Compare(ix, iy);
		}

		public int Compare(int x, int y)
		{
			return x - y;
		}
	}

	
	// ISerializable
	public virtual void Serialize(GenericWriter writer)
	{
		writer.Write((int)0); // version

		writer.Write((string)m_PlanetID);
		writer.Write((string)m_PlanetName);
		writer.Write((int)m_Size);
		writer.Write((int)m_Climate);
		writer.Write((int)m_Mineral);
		writer.Write((int)m_Lifeform);
		writer.Write((int)m_PlanetState);
		writer.Write((int)m_Natives);

		writer.Write((int)m_Status);
		writer.Write((int)m_Richness);
		writer.Write((int)m_Orbit);
		writer.Write((bool)m_Intel);
		writer.Write((bool)m_Giant);
		writer.Write((bool)m_AutoMode);

		writer.Write((StarSystem)m_Solar);
		writer.Write((Kingdom)m_Kingdom);
		writer.Write((bool)m_HugeShip);

		writer.Write((double)m_ProdPower);
		writer.Write((double)m_DevPower);
		writer.Write((double)m_FarmPower);
		writer.Write((double)m_BaseProd);
		writer.Write((double)m_BaseDev);
		writer.Write((double)m_BaseFarm);

		writer.Write((double)m_MaxPop);

		writer.Write((int)m_Population.Length);
		for (int i = 0; i < m_Population.Length; i++)
			writer.Write((double)m_Population[i]);

		writer.Write((double)m_GoldAmt);
		writer.Write((double)m_GoldFactor);
		writer.Write((double)m_BaseMarine);
		writer.Write((double)m_BaseDefense);
		writer.Write((double)m_DefendPow);
		writer.Write((double)m_Speed);

		writer.Write((int)m_Facilities.Count);
		if (m_Facilities.Count > 0)
		{
			for (int i = 0; i < m_Facilities.Count; i++)
			{
				writer.Write((int)m_Facilities[i].pName);
				writer.Write((Building)m_Facilities[i].pBuild);
				writer.Write((bool)m_Facilities[i].pInEffect);
				writer.Write((bool)m_Facilities[i].pObsolete);
				writer.Write((string)m_Facilities[i].DisplayName);
				writer.Write((double)m_Facilities[i].BuildCost);
				writer.Write((double)m_Facilities[i].OwnCost);
				writer.Write((bool)m_Facilities[i].pRepeat);
			}
		}

		writer.Write((int)m_DevObj.Count);
		if (m_DevObj.Count > 0)
			for (int i = 0; i < m_DevObj.Count; i++)
			{
				writer.Write((string)m_DevObj.GetType().FullName);
			}

		// m_Dispatch and m_Warship will be restored by running 
		// UpdateDispatchables() and UpdateWarrables() respectively
	}

	public virtual void Deserialize(GenericReader reader)
	{
		int version = reader.ReadInt();

		m_PlanetID = reader.ReadString();
		m_PlanetName = reader.ReadString();
		m_Size = (PlanetSize)reader.ReadInt();
		m_Climate = (PlanetClimate)reader.ReadInt();
		m_Mineral = (PlanetMineral)reader.ReadInt();
		m_Lifeform = (PlanetLifeform)reader.ReadInt();
		m_PlanetState = (PlanetState)reader.ReadInt();
		m_Natives = (PlanetNatives)reader.ReadInt();

		m_Status = (PlanetStatus)reader.ReadInt();
		m_Richness = (PlanetState)reader.ReadInt();
		m_Orbit = reader.ReadInt();
		m_Intel = reader.ReadBool();
		m_Giant = reader.ReadBool();
		m_AutoMode = reader.ReadBool();

		m_Solar = reader.ReadStarSystem();
		m_Kingdom = reader.ReadKingdom();
		m_HugeShip = reader.ReadBool();

		m_ProdPower = reader.ReadDouble();
		m_DevPower = reader.ReadDouble();
		m_FarmPower = reader.ReadDouble();
		m_BaseProd = reader.ReadDouble();
		m_BaseDev = reader.ReadDouble();
		m_BaseFarm = reader.ReadDouble();

		m_MaxPop = reader.ReadDouble();

		int count = reader.ReadInt();
		for (int i = 0; i < count; i++)
			m_Population[i] = reader.ReadDouble();

		m_GoldAmt = reader.ReadDouble();
		m_GoldFactor = reader.ReadDouble();
		m_BaseMarine = reader.ReadDouble();
		m_BaseDefense = reader.ReadDouble();
		m_DefendPow = reader.ReadDouble();
		m_Speed = reader.ReadDouble();

		count = reader.ReadInt();
		m_Facilities = new List<PlanetFacility>(count);
		PlanetFacility pf;

		if (count > 0)
			for (int i = 0; i < count; i++)
			{
				pf = new PlanetFacility((GameEnums.Facilities)reader.ReadInt(), reader.ReadBuilding(), reader.ReadBool(), reader.ReadBool());
				pf.DisplayName = reader.ReadString();
				pf.BuildCost = reader.ReadDouble();
				pf.OwnCost = reader.ReadDouble();
				pf.pRepeat = reader.ReadBool();

				m_Facilities.Add(pf);
				m_Facilities.Sort(new FacilitySorter());
			}

		count = reader.ReadInt();
		m_DevObj = new List<IBuildable>();
		if (count > 0)
		{
			Type[] alltypes = Core.GAssembly.GetTypes();

			for (int i = 0; i < count; i++)
			{
				Type ty = null;
				object o = null;
				string fullname = reader.ReadString();

				for (int j = 0; j < alltypes.Length; j++)
					if (alltypes[j].FullName == fullname)
					{
						ty = alltypes[j];
						break;
					}

				if (ty != null)
					o = Activator.CreateInstance(ty);

				if (o != null && o is IBuildable)
					m_DevObj.Add(o as IBuildable);
			}
		}

		//UpdateDispatchables();
		//UpdateWarrables();
	}


	// class methods
	public void OnTurn()
	{
		m_DevPower += 50.0;

		// to re-fill m_Dev if in auto-mode.
		if (m_AutoMode && m_DevObj.Count == 0)
			ToFillDevQueue(m_Kingdom.FillDev);					// as new facility may be available in each turn, refill less is better

		foreach (PlanetFacility pf in m_Facilities)
			if (pf.pInEffect)
				pf.pBuild.OnTurn(this);

		m_NewlyOwned = new List<PlanetFacility>();			// reset arraylist
		ConsumeDevPower();

		foreach (PlanetFacility pfy in m_NewlyOwned)
		{
			//Debug.Log("Newly Developed Facility: " + pfy.pName);
			pfy.pBuild.OnBuilt(this);
		}
	}

	public void OnEndTurn()
	{
		//List<Building>[] laTurn = UpdatePlanetFacilities();
		UpdatePlanetFacilities();

		// to remove unqualified repeatables
		if (m_DevObj.Count > 0)
		{
			for (int i = 1; i < m_DevObj.Count; i++)						// m_DevObj[0] is not checked
				if (m_DevObj[i] is IRepeatable && !((IRepeatable)m_DevObj[i]).CheckRepeat(this))
					m_DevObj.RemoveAt(i--);
		}

		UpdateDispatchables();
		UpdateWarrables();

	}

	public bool ConsumeDevPower()
	{
		if (m_DevObj.Count == 0)
		{
			// to consume power
			m_DevPower = 0.0;

			return false;
		}

		m_DevObj[0].OwnCost += m_DevPower;		// productivity power consumed
		m_DevPower = 0.0;											// reset turn devpower
		
		if (this.Kingdom == Core.galaxyKingdoms[0])
			Debug.Log("Star: " + this.m_PlanetName + ", OwnCost: " + (m_DevObj[0]).OwnCost.ToString() + ", BuildCost: " + m_DevObj[0].BuildCost.ToString());

		if (m_DevObj[0] is IDispatchable && m_DevObj[0].OwnCost >= m_DevObj[0].BuildCost)
		{
			if (m_DevObj[0] is IRepeatable)
				((IRepeatable)m_DevObj[0]).CheckRepeat(this);

			if (m_DevObj[0] is Fleet)				// satellite and freighters will not be instantiated
			{
				object o = Activator.CreateInstance(m_DevObj[0].GetType());
				IDispatchable dp = o as IDispatchable;
				PrepareNewFleet(dp as Fleet);

				Debug.Log("New: " + this.Kingdom.Race.RaceType.ToString() + " #" + ((Fleet)dp).FleetID.ToString() + " " + m_DevObj[0].GetType().ToString());
			}
			else
			{
				// Freighters and Satellite
				if (Kingdom.Dispatchables.ContainsKey(m_DevObj[0].GetType()))
					Kingdom.Dispatchables[m_DevObj[0].GetType()] = (int)Kingdom.Dispatchables[m_DevObj[0].GetType()] + 1;
				else
					Kingdom.Dispatchables.Add(m_DevObj[0].GetType(), 1);
			}

			m_DevObj[0].OwnCost = 0.0;			// to reset static version template buildable for next use, just in case
			m_DevObj.RemoveAt(0);

			return true;			// as m_DevObj[0] is removed from array, must return here
		}

		if (m_DevObj[0] is Fleet && m_DevObj[0].OwnCost >= m_DevObj[0].BuildCost)			// is Fleet but not IDispatchable
		{
			if (m_DevObj[0] is IRepeatable)
				((IRepeatable)m_DevObj[0]).CheckRepeat(this);

			object o = Activator.CreateInstance(m_DevObj[0].GetType());
			Fleet fl = o as Fleet;
			PrepareNewFleet(fl);

			Debug.Log("New: " + this.Kingdom.Race.RaceType.ToString() + " #" + fl.FleetID.ToString() + " " + m_DevObj[0].GetType().ToString());

			m_DevObj[0].OwnCost = 0.0;			// to reset static version template fleet for next use, just in case
			m_DevObj.RemoveAt(0);

			return true;			// as m_DevObj[0] is removed from array, must return here
		}
	
		if (m_DevObj[0] is PlanetFacility && m_DevObj[0].OwnCost >= m_DevObj[0].BuildCost)
		{
			PlanetFacility afterpf = (PlanetFacility)m_DevObj[0];

			if (afterpf.pBuild is IRepeatable)
					afterpf.pRepeat = ((IRepeatable)afterpf.pBuild).CheckRepeat(this);
				
			afterpf.pInEffect = true;			// facility development completed, facility in effect, and not to repeat development
			afterpf.OwnCost = 0.0;				// reset for IRepeatables
			m_NewlyOwned.Add((PlanetFacility)m_DevObj[0]);

			if (afterpf.pBuild is ColonyBase)
			{
				this.Solar.ColonyBase[2] -= 1;			// tracking developing colony base
				this.Solar.ColonyBase[3] -= 1;
			}

			m_DevObj.RemoveAt(0);

			// to detect newly available m_DevObj[0], after old m_DevObj being removed
			if (m_DevObj.Count > 0 && m_DevObj[0] is PlanetFacility && ((PlanetFacility)m_DevObj[0]).pBuild is ColonyBase && this.Solar.ColonyBase[3] <= 0)		// player colony base is added in camera control
				this.Solar.ColonyBase[2] += 1;

			return true;			// as m_DevObj[0] is removed from array, must return here 
		}

		return false;
	}

	public void PrepareNewFleet(Fleet fl)
	{
		fl.FleetID = ++Galaxy.FleetSerial;
		fl.Location = m_Solar.Location;
		fl.Belong = m_Kingdom;
		fl.Home = m_Solar;
		fl.Dest = m_Solar;

		m_Kingdom.Fleets.Add(fl);
		m_Solar.Docking.Add(fl);
		m_Solar.Docking.Sort(new Fleet.SortRankSorter());

		Core.Fleets.Add(fl.FleetID, fl);				// keep tracking of all ships for save/load
	}

	public bool ToFillDevQueue(int fill)
	{
		if (m_DevObj.Count >= Core.MaxDevQueue)			// development queue is full
			return false;

		int length = fill;
		List<IBuildable> lbd1 = new List<IBuildable>();		// for PlanetFacility
		List<IBuildable> lbd2 = new List<IBuildable>();		// for IDispatchable
		List<IBuildable> lbd3 = new List<IBuildable>();		// for IWarrable

		if (fill <= 0 || fill > Core.MaxDevQueue)
			length = Core.MaxDevQueue;

		length = Math.Min(length, Core.MaxDevQueue - m_DevObj.Count);		// in case m_DevObj is not empty

		// to add available facilities to list
		foreach (PlanetFacility pf in m_Facilities)
		{
			if (pf.pBuild is IRepeatable)
				pf.pRepeat = ((IRepeatable)pf.pBuild).CheckRepeat(this);

			if (pf.pRepeat || (!pf.pRepeat && !pf.pInEffect && !pf.pObsolete))
				lbd1.Add(pf as IBuildable);
		}

		// to add available dispatchables to list
		foreach (IBuildable bd in m_Dispatch)
		{
			if (bd is IRepeatable)
				if (!((IRepeatable)bd).CheckRepeat(this))
					continue;

			lbd2.Add(bd);
		}

		foreach (IBuildable ws in m_Warship)
		{
			if (!((IRepeatable)ws).CheckRepeat(this))
				continue;

			lbd3.Add(ws);
		}

		if (lbd1.Count == 0 && lbd2.Count == 0 && lbd3.Count == 0)
			return false;				// no more available facility to develop

		length = Math.Min(length, lbd1.Count + lbd2.Count);			// there might not be enough facilities to develop

		for (int i = 0; i < length; i++)
		{
			int rand = 0;

			if (lbd1.Count > 0 && lbd2.Count > 0)
			{
				if (0.16 > Utility.RandomDouble())		// chance to pick dispatchables
				{
					rand = Utility.Random(lbd2.Count);
					m_DevObj.Add(lbd2[rand]);
					lbd2.RemoveAt(rand);
				}
				else
				{
					rand = Utility.Random(lbd1.Count);
					m_DevObj.Add(lbd1[rand]);
					lbd1.RemoveAt(rand);
				}
			}
			else if (lbd1.Count > 0)
			{
				rand = Utility.Random(lbd1.Count);
				m_DevObj.Add(lbd1[rand]);
				lbd1.RemoveAt(rand);
			}
			else if (0.25 > Utility.RandomDouble() && lbd2.Count > 0)
			{
				rand = Utility.Random(lbd2.Count);
				m_DevObj.Add(lbd2[rand]);
				lbd2.RemoveAt(rand);
			}
			else if (lbd3.Count > 0)
			{
				// reserved for warships
				if (0.25 > Utility.RandomDouble())
				{
					rand = Utility.Random(lbd3.Count);
					m_DevObj.Add(lbd3[rand]);
					lbd3.RemoveAt(rand);
				}
			}

		}

		return true;
	}

	public double CalPopulation()
	{
		return m_Population[0] + m_Population[1] + m_Population[2];
	}

	public bool RemoveFacility(Building building)
	{
		if (m_Facilities.Count == 0)
			return false;

		for (int i = 0; i < m_Facilities.Count; i++)
			if (m_Facilities[i].pBuild.Name == building.Name)
				m_Facilities.RemoveAt(i--);

		return true;
	}

	public bool UpdateWarrables()
	{
		bool toadd = false;
		int cnt;

		m_Warship = new List<IBuildable>();				// to reset arraylist

		foreach (IBuildable ws in Technology.WTable)
		{
			if (ws is IRepeatable && ((IRepeatable)ws).CheckRepeat(this))
				toadd = true;

			cnt = 0;

			if (toadd && ws is Fleet)
			{
				toadd = false;

				foreach (GameEnums.TechName tn in ((Fleet)ws).MotherTech)
					foreach (Kingdom.KingdomTech kt in m_Kingdom.Tech)
						foreach (Kingdom.TechByCost tb in kt.plTech)
						{
							if (tb.pTech.Name == tn && tb.pStatus == Technology.TechStatus.Owned)
								cnt++;
						}

				if (cnt == ((Fleet)ws).MotherTech.Length)
				{
					cnt = 0;

					foreach (GameEnums.Facilities ft in ((Fleet)ws).MotherFacility)
						foreach (PlanetFacility pfy in m_Facilities)
						{
							if (ft == pfy.pBuild.Name && (pfy.pInEffect || pfy.pObsolete))
								cnt++;
						}

					if (cnt == ((Fleet)ws).MotherFacility.Length)
						toadd = true;
				}

			}

			if (toadd)
			{
				// create template warships for development
				object o = Activator.CreateInstance(ws.GetType());
				IBuildable bd = o as IBuildable;
				m_Warship.Add(bd);
			}
		}

		return true;
	}

	public bool UpdateDispatchables()
	{
		bool toadd = false;
		int cnt;

		m_Dispatch = new List<IBuildable>();			// to reset arraylist

		foreach (IBuildable dp in Technology.DTable)
		{
			if (dp is IRepeatable && ((IRepeatable)dp).CheckRepeat(this))
				toadd = true;

			cnt = 0;

			if (toadd && dp is IDispatchable)
			{
				toadd = false;

				foreach (GameEnums.TechName tn in ((IDispatchable)dp).MotherTech)
					foreach (Kingdom.KingdomTech kt in m_Kingdom.Tech)
						foreach (Kingdom.TechByCost tb in kt.plTech)
						{
							if (tb.pTech.Name == tn && tb.pStatus == Technology.TechStatus.Owned)
								cnt++;
						}

				if (cnt == ((IDispatchable)dp).MotherTech.Length)
					toadd = true;
			}

			if (toadd)
			{
				// create a new instance instead of using the static version, such that buildcost can be tracked
				object o = Activator.CreateInstance(dp.GetType());
				IBuildable bd = o as IBuildable;
				m_Dispatch.Add(bd);

			}
		}

		return toadd;
	}

	public List<Building>[] UpdatePlanetFacilities()
	{
		List<Building> lbuild = new List<Building>();			// to store newly available facilities
		List<Building> lobs = new List<Building>();				// to store obsoleted facilities
		bool toGo;

		foreach (Building bd in Technology.BTable)
		{
			toGo = true;

			foreach (PlanetFacility pf in m_Facilities)
				if (pf.pBuild.Name == bd.Name || pf.pObsolete)
					toGo = false;

			if (!toGo)
				continue;

			int cnt = 0;

			foreach (GameEnums.TechName tn in bd.MotherTech)
			{
				foreach (Kingdom.KingdomTech kt in m_Kingdom.Tech)
					foreach (Kingdom.TechByCost tb in kt.plTech)
					{
						if (tb.pTech.Name == tn && tb.pStatus == Technology.TechStatus.Owned)
							cnt++;
					}
			}

			if (cnt == bd.MotherTech.Length)
				lbuild.Add(bd);									// all facilities qualified to be owned

			foreach (GameEnums.Facilities obf in bd.Obsolete)
				foreach (PlanetFacility plf in m_Facilities)
				{
					if (plf.pName == obf)					// obsoleted facilities found)
						lobs.Add(plf.pBuild);
				}
		}
		
		if (lbuild.Count == 0)
			return new List<Building>[]{lbuild, lobs};

		Building bld;
		object o;

		foreach (Building bdn in lbuild)
		{
			if (bdn is IRepeatable && !((IRepeatable)bdn).CheckRepeat(this))
				continue;				// not to development unqualified repeatables

			bld = bdn;

			if (bdn is IConstructable)				// instantiate a non-static version of the building
			{
				o = Activator.CreateInstance(bdn.GetType());			// non-static version of building

				bld = o as Building;
				bld.Planet = this;

				bld.FacilityID = ++Galaxy.FacilitySerial;				// for both static and non-static buildings
				Core.Buildings.Add(bld.FacilityID, bld);
			}

			// if id is not assigned load/save cannot be done correctly
			//if (bld.FacilityID == 0)
			//{
			//  bld.FacilityID = ++Galaxy.FacilitySerial;				// for both static and non-static buildings
			//  Core.Buildings.Add(bld.FacilityID, bld);
			//}

			m_Facilities.Add(new PlanetFacility(bld.Name, bld, false, false));
			m_Facilities.Sort(new FacilitySorter());
		}

		return new List<Building>[]{lbuild, lobs};
	}

	public bool GeneratePlanet( int orbit, Point3D loc, bool isGiant, PlanetType pltype )
	{
		bool isRich = false;

		// to set visual variables
		m_Speed = Utility.RandomMinMax(6, 12);		// planet's rotational speed around sun

		// initialize flags
		m_PlanetState = PlanetState.None;
		m_Natives = PlanetNatives.None;
		m_Richness = PlanetState.None;

		// to create a Gas Giant occupying the orbit
		if (isGiant)
		{
			m_Size = PlanetSize.None;
			m_Climate = PlanetClimate.Uninhabitable;
			m_Mineral = PlanetMineral.UltraPoor;
			m_Lifeform = PlanetLifeform.None;

			return true;
		}

		// to generate planet size, PlanetSize.None will not be generated
		m_Size = (PlanetSize)Utility.RandomMinMax(1, 5);		// PlanetSize.Other will not be generated

		// to generate planet climate, mineral richness and planet life form
		if (pltype == PlanetType.Extreme)
		{
			m_Climate = (PlanetClimate)Utility.Random(1);
			m_Mineral = (PlanetMineral)Utility.RandomMinMax(1, 4);
			m_Lifeform = PlanetLifeform.None;
		}
		else if (pltype == PlanetType.Poor)
		{
			m_Climate = (PlanetClimate)Utility.RandomMinMax(1, 3);
			m_Mineral = (PlanetMineral)Utility.RandomMinMax(1, 3);
			m_Lifeform = (PlanetLifeform)Utility.Random(2);
		}
		else if (pltype == PlanetType.Normal)
		{
			m_Climate = (PlanetClimate)Utility.RandomMinMax(2, 6);
			m_Mineral = (PlanetMineral)Utility.RandomMinMax(1, 5);
			m_Lifeform = (PlanetLifeform)Utility.RandomMinMax(1, 4);
		}
		else if (pltype == PlanetType.Rich)
		{
			if ( 0.72 > Utility.RandomDouble() )
				m_Climate = (PlanetClimate)Utility.RandomMinMax(6, 8);
      else
			  m_Climate = (PlanetClimate)Utility.RandomMinMax(4, 7);

			m_Mineral = (PlanetMineral)Utility.RandomMinMax(3, 5);
			m_Lifeform = (PlanetLifeform)Utility.RandomMinMax(2, 5);
		}
		else
		{
			m_Climate = PlanetClimate.Gaia;
			m_Mineral = PlanetMineral.UltraRich;
			m_Lifeform = PlanetLifeform.High;
		}

		// to generate planet gravity state
		switch (Utility.Random(5))
		{
			case 0:
			case 1:
			case 2: m_PlanetState |= PlanetState.None; break;
			case 3: m_PlanetState |= PlanetState.LowG; break;
			case 4: m_PlanetState |= PlanetState.HeavyG; break;

			default: throw new ArgumentException();
		}

		// to generate geographical pole status
		if (Utility.RandomBool())
			m_PlanetState |= PlanetState.BadGeoPole;

		// to generate magnetic pole status
		if (Utility.RandomBool())
			m_PlanetState |= PlanetState.BadMagPole;

		// to generate atmospheric status
		switch (Utility.Random(6))
		{
			case 0:
			case 1:
			case 2:
			case 3: m_PlanetState |= PlanetState.None; break;
			case 4: m_PlanetState |= PlanetState.Radiation; break;
			case 5: m_PlanetState |= PlanetState.ToxicGas; break;

			default: throw new ArgumentException();
		}

		// to generate virus infected status, 0.06 here is the chance that planet is infected
		if (0.06 > Utility.RandomDouble())
			m_PlanetState |= PlanetState.WithVirus;

		// chances that the planet is with artifacts
		if (!isRich && 0.01 > Utility.RandomDouble())
		{
			m_PlanetState |= PlanetState.WithArtifact;
			m_Richness = PlanetState.WithArtifact;
			isRich = true;
		}

		// chances that the planet is rare metal rich
		if (!isRich && 0.025 > Utility.RandomDouble())
		{
			m_PlanetState |= PlanetState.MetalRich;
			m_Richness = PlanetState.MetalRich;
			isRich = true;
		}

		// chances that the planet is gem rich
		if (!isRich && 0.1 > Utility.RandomDouble())
		{
			m_PlanetState |= PlanetState.GemRich;
			m_Richness = PlanetState.GemRich;
			isRich = true;
		}

		// chances that the planet is gold rich
		if (!isRich && 0.125 > Utility.RandomDouble())
		{
			m_PlanetState |= PlanetState.GoldRich;
			m_Richness = PlanetState.GoldRich;
			isRich = true;
		}

		// chances that planet is with natives
		if (m_Lifeform > PlanetLifeform.Low && 0.2 > Utility.RandomDouble())
		{
			m_PlanetState |= PlanetState.WithIntel;
			m_Intel = true;

			// to decide the type of natives
			switch (Utility.Random(7))
			{
				case 0: m_Natives |= PlanetNatives.None; break;
				case 1: m_Natives |= PlanetNatives.Magic; break;
				case 2: m_Natives |= PlanetNatives.Hostle; break;
				case 3: m_Natives |= PlanetNatives.Telepathy; break;
				case 4: m_Natives |= PlanetNatives.Foreknowledge; break;
				case 5: m_Natives |= PlanetNatives.RangeAttack; break;
				case 6: m_Natives |= PlanetNatives.SpaceFight; break;

				default: throw new ArgumentException();
			}
		}

		// chances that a space guardian protecting the planet
		if (0.6 > Utility.RandomDouble())
			m_PlanetState |= PlanetState.WithGuardian;

		return true;
	}


	// static methods
	public static string PreparePlanetText(Planet pl)
	{
		string plStr = "";

		if (pl.Giant)
			return "a Gas Giant";

		plStr = Utility.SplitPadName("Name:", pl.PlanetName, 25) + "\n"
			+ Utility.SplitPadName("Climate:", pl.Climate.ToString(), 25) + "\n"
			+ Utility.SplitPadName("Size:", pl.Size.ToString(), 25) + "\n"
			+ Utility.SplitPadName("Mineral:", pl.Mineral.ToString(), 25) + "\n"
			+ Utility.SplitPadName("Life:", pl.Lifeform.ToString(), 25);

		return plStr;
	}

}
